using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HumanPlayer : PlayerBase
{
	public Transform racket;

	public Transform fire;

	public bool autoPilot;

	public FloatRange autoSpeed;

	public float pingTime;

	public FloatRange fingerSpeedRange = default(FloatRange);

	protected bool buttonDown;

	protected bool hitBall;

	protected Plane movePlane;

	protected GestureTracker gestureTracker = new GestureTracker();

	protected float time;

	protected GravityFlight serveFlight = new GravityFlight();

	protected Vector3 ballLandingPos;

	protected float maxDistanceBehindBallInServe;

	private bool inHitBall;

	public int markerHits;

	public CameraController cam
	{
		get
		{
			return base.match.cameraController;
		}
	}

	public Vector3 highestPositionAfterRebound { get; protected set; }

	protected RacketSettings.Racket racketDef
	{
		get
		{
			return base.match.ui.racketButton.currentRacket;
		}
	}

	public Vector3 racketPos
	{
		get
		{
			return racket.position;
		}
	}

	public override void Init(int teamTag, int tableSide, MatchController match)
	{
		if (Application.platform != 0)
		{
			autoPilot = false;
		}
		base.Init(teamTag, tableSide, match);
		movePlane = new Plane(Vector3.up, Vector3.up * (Table.tabletopy + 0.2f));
		gestureTracker.MaxQueueSize = 20;
		gestureTracker.minDistanceScreen = 0f;
		gestureTracker.minDistance = 0f;
		fire.gameObject.SetActive(false);
		match.ui.cameraButton.onCameraChanged += OnCameraChanged;
		OnCameraChanged();
	}

	private void OnCameraChanged()
	{
		HumanPlayerCamera currentCameraBehaviour = base.match.ui.cameraButton.currentCameraBehaviour;
		currentCameraBehaviour.humanPlayer = this;
		cam.SetCameraBehaviour(currentCameraBehaviour);
	}

	private void Update()
	{
		if (InputControl.buttonDown)
		{
			buttonDown = true;
			gestureTracker.Clear();
		}
		if (InputControl.buttonUp)
		{
			buttonDown = false;
			gestureTracker.Clear();
		}
		time += Time.deltaTime;
		if (buttonDown)
		{
			MoveRacketToPos(Input.mousePosition);
		}
		if (!(base.ball == null))
		{
			if (base.isInServe)
			{
				UpdateServeBall();
			}
			CheckIfHitBall();
		}
	}

	private void UpdateServeBall()
	{
		Vector3 position = racket.position;
		float magnitude = CalculateVelocityFromGestureTracker().magnitude;
		float num = Mathf.Lerp(4f, 1f, Mathf.InverseLerp(0f, 2f, magnitude));
		position.x = Mathf.Lerp(base.ball.position.x, racket.position.x, Time.deltaTime * num);
		position.x = Mathf.Clamp(position.x, 0f - Table.halfwidth, Table.halfwidth);
		position.z = (float)base.tableSide * Table.halfLength;
		position.y = Table.netHeight + Table.tabletopy;
		serveFlight.timeOfFlight += Time.deltaTime;
		serveFlight.timeOfFlight = Mathf.Repeat(serveFlight.timeOfFlight, 2f * serveFlight.apexTime);
		position.y = serveFlight.Position(serveFlight.timeOfFlight).y;
		base.ball.position = position;
		maxDistanceBehindBallInServe = Mathf.Max((float)base.tableSide * (racket.position.z - position.z), maxDistanceBehindBallInServe);
	}

	private void CheckIfHitBall()
	{
		if (inHitBall || base.ball == null || (base.isInServe && maxDistanceBehindBallInServe < base.ball.myRadius * 1.1f) || (!base.isInServe && base.ball.flightPlan.flightTime < base.ball.flightPlan.timeToLand))
		{
			return;
		}
		if (!base.isInServe)
		{
			BallFlightPlan.FlightPlanStage reboundStage = base.ball.flightPlan.reboundStage;
			if (reboundStage != null)
			{
				float num = reboundStage.flight.apexTime * 2f;
				Vector3 pos = reboundStage.flight.Position(num);
				if (Table.IsPosOnTable(pos) && base.ball.flightPlan.flightTime > base.ball.flightPlan.GetDurationTillStage(reboundStage) + num)
				{
					return;
				}
			}
		}
		LinkedList<FingerPos> queue = gestureTracker.GetQueue();
		Vector3 vector = racket.position;
		Vector3 a = vector;
		float deltaTime = Time.deltaTime;
		if (queue.Count > 0)
		{
			LinkedListNode<FingerPos> last = queue.Last;
			FingerPos value = last.Value;
			FingerPos fingerPos = ((last.Previous == null) ? value : last.Previous.Value);
			deltaTime = value.deltaTime;
			a = fingerPos.pos;
			vector = value.pos;
		}
		Vector3 position = base.ball.position;
		Vector3 prevPosition = base.ball.prevPosition;
		float myRadius = base.ball.myRadius;
		float num2 = 0.5714794f;
		float num3 = 1.201868f;
		Vector3 vector2 = position - vector;
		float num4 = (float)base.tableSide * a.z - (float)base.tableSide * prevPosition.z;
		float num5 = (float)base.tableSide * vector.z - (float)base.tableSide * position.z;
		float num6 = 7f * base.ball.myRadius;
		float myRadius2 = base.ball.myRadius;
		float num7 = Vector3.Distance(a, vector) / deltaTime / Table.halfwidth;
		float num8 = 0.5f;
		if (num7 < num8)
		{
			num6 = base.ball.myRadius;
		}
		if (num4 > num5 && num5 <= num6 && num4 >= 0f - base.ball.myRadius && Mathf.Abs(vector2.x) <= num2 * racketDef.paddleWidth && Mathf.Abs(vector2.y) <= num3 * 2f)
		{
			hitBall = true;
		}
		if (hitBall && num5 <= myRadius2)
		{
			hitBall = false;
			base.ball.flightPlan.active = false;
			base.ball.MakeKinematic();
			StartCoroutine(DoHitBall());
		}
	}

	protected IEnumerator DoHitBall()
	{
		inHitBall = true;
		float time = 0f;
		float maxDuration = 0.03f;
		Vector3 startRacketPos = racket.position;
		Vector3 startBallPos = base.ball.position;
		while (time < maxDuration)
		{
			time += RealTime.deltaTime;
			yield return null;
		}
		HitBall();
		inHitBall = false;
	}

	private void MoveRacketToPos(Vector3 mousePosition)
	{
		Vector3 vector = ToWorldPositionFromScreenPoint(mousePosition);
		if (!(vector == Vector3.zero))
		{
			LinkedList<FingerPos> queue = gestureTracker.GetQueue();
			if (queue.Last != null && queue.Last.Value.deltaTime > 4f * Time.deltaTime)
			{
				queue.Clear();
			}
			gestureTracker.AddPos(new FingerPos
			{
				pos = vector,
				screenPos = mousePosition,
				deltaTime = Time.deltaTime,
				realTime = time
			});
			vector.x = Mathf.Min(Mathf.Abs(vector.x), Table.halfwidth * 2f) * Mathf.Sign(vector.x);
			racket.position = vector;
			Ball ball = base.match.ball;
			float num = ball.position.x - racket.position.x;
			racket.rotation = Quaternion.Lerp(racket.rotation, Quaternion.AngleAxis((float)MathEx.SignZeroPositive(num) * Mathf.Lerp(0f, 1f, 4f * Mathf.Abs(num) / Table.halfwidth) * -70f, Vector3.forward), Time.deltaTime * 20f);
		}
	}

	public Vector3 CalculateVelocityFromGestureTracker()
	{
		if (autoPilot)
		{
			Vector3 vector = new Vector3(0f, base.ball.position.y, (float)(-base.tableSide) * Table.halfLength * 0.5f) - base.ball.position;
			RacketSettings.Racket currentRacket = base.match.ui.racketButton.currentRacket;
			float max = currentRacket.fingerSpeedRange.max;
			if (currentRacket.type == RacketSettings.SettingsType.RangeSettings)
			{
				max = currentRacket.ranges[currentRacket.ranges.Count - 1].fingerSpeedRange.max;
			}
			max *= Random.Range(autoSpeed.min, autoSpeed.max);
			return vector.normalized * max;
		}
		if (gestureTracker.Count() < 2)
		{
			base.match.ui.debugLabels[0].text = "Speed: " + 0;
			base.match.ui.debugLabels[1].text = "Count: " + gestureTracker.Count();
			return Vector3.zero;
		}
		LinkedList<FingerPos> queue = gestureTracker.GetQueue();
		FingerPos value = queue.First.Value;
		Vector3 vector2 = ToWorldPositionFromScreenPoint(queue.Last.Value.screenPos);
		Vector3 vector3 = ToWorldPositionFromScreenPoint(value.screenPos);
		LinkedListNode<FingerPos> linkedListNode = queue.Last;
		Vector3 vector4 = Vector3.zero;
		int num = 0;
		int num2 = 2;
		while (linkedListNode.Previous != null)
		{
			LinkedListNode<FingerPos> previous = linkedListNode.Previous;
			Vector3 vector5 = ToWorldPositionFromScreenPoint(linkedListNode.Value.screenPos);
			Vector3 vector6 = ToWorldPositionFromScreenPoint(previous.Value.screenPos);
			Vector3 vector7 = vector5 - vector6;
			Vector3 normalized = vector7.normalized;
			vector4 += vector7 / linkedListNode.Value.deltaTime;
			num++;
			if (Mathf.Abs(normalized.z) > 0.2f && Mathf.Sign(normalized.z) == (float)base.tableSide && num > num2)
			{
				break;
			}
			linkedListNode = previous;
		}
		vector4 /= (float)num;
		if (Application.platform == RuntimePlatform.OSXEditor)
		{
			vector4 = vector4.normalized * vector4.magnitude / 1.7f;
		}
		base.match.ui.debugLabels[0].text = string.Concat("Speed: ", vector4, " m: ", vector4.magnitude / Table.length);
		base.match.ui.debugLabels[1].text = "Count: " + num;
		return vector4;
	}

	private Vector3 ToWorldPositionFromScreenPoint(Vector3 screenPoint)
	{
		Camera component = cam.GetComponent<Camera>();
		Ray ray = component.ScreenPointToRay(screenPoint);
		float enter = 0f;
		if (!movePlane.Raycast(ray, out enter))
		{
			return Vector3.zero;
		}
		Vector3 point = ray.GetPoint(enter);
		float num = 0.1f * Table.halfLength;
		if (Mathf.Sign(point.z) != (float)base.tableSide || Mathf.Abs(point.z) < num)
		{
			point.z = num * (float)base.tableSide;
		}
		return point;
	}

	public void HitBall()
	{
		RacketSettings.Racket racket = racketDef;
		Ball ball = base.ball;
		if (base.isInServe)
		{
			Vector3 position = base.ball.position;
			position.y = Mathf.Max(this.racket.position.y, position.y);
			base.ball.position = position;
		}
		base.isInServe = false;
		base.match.ui.ballMarker.Hide();
		fire.gameObject.SetActive(false);
		gestureTracker.Clear();
		MultiplayerMatchController multiplayerMatchController = base.match as MultiplayerMatchController;
		if (multiplayerMatchController == null)
		{
			float durationTillStage = ball.flightPlan.GetDurationTillStage(ball.flightPlan.reboundStage);
			float timeScale = durationTillStage / (durationTillStage + pingTime * 0.001f / Time.timeScale);
			ball.flightPlan.timeScale = timeScale;
		}
		hitBall = false;
	}

	public override void BallRemovedFromGame()
	{
		base.BallRemovedFromGame();
		base.isInServe = false;
		base.match.ui.ballMarker.Hide();
	}

	public override void StartServe(Ball ball)
	{
		ball.TakeBall();
		base.ball = ball;
		hitBall = false;
		base.isInServe = true;
		maxDistanceBehindBallInServe = 0f;
		ballLandingPos = new Vector3(0f, Table.tabletopy, (float)base.tableSide * Table.halfLength);
		base.match.ui.ballMarker.Hide();
		Vector3 position = racket.position;
		position.y = Table.tabletopy;
		position.x = Mathf.Clamp(position.x, 0f - Table.halfwidth, Table.halfwidth);
		ball.position = position;
		serveFlight.Fire(position, ParabolicFlight.initialVelocityForSimpleCase(position, position, Table.tabletopy + 0.3f), Physics.gravity);
		base.match.ui.ballFollower.fire.SetActive(false);
		if (autoPilot)
		{
			StopAllCoroutines();
			StartCoroutine(MoveToPos(new Vector3(0f, racket.position.y, (float)base.tableSide * Table.halfLength * 1.3f), 30f));
			this.WaitAndExecute(0.5f, HitBall);
		}
	}

	public override void ReceiveServe()
	{
		ballLandingPos = new Vector3(0f, Table.tabletopy, (float)base.tableSide * Table.halfLength);
		base.ball = null;
		base.match.ui.ballMarker.Hide();
		base.match.ui.ballFollower.fire.SetActive(false);
		hitBall = false;
	}

	public override void ReceiveBall(Ball ball)
	{
		base.ball = ball;
		hitBall = false;
		ballLandingPos = ball.flightPlan.landingPos;
		BallFlightPlan.FlightPlanStage reboundStage = ball.flightPlan.reboundStage;
		if (reboundStage != null)
		{
			Vector3 vector = reboundStage.flight.Position(reboundStage.flight.apexTime);
			highestPositionAfterRebound = vector;
			float durationTillStage = ball.flightPlan.GetDurationTillStage(reboundStage);
		}
		if (autoPilot && Table.IsPosOnTable(ball.flightPlan.landingPos))
		{
			BallFlightPlan.FlightPlanStage flightPlanStage = ball.flightPlan.LastStage();
			float durationTillStage2 = ball.flightPlan.GetDurationTillStage(flightPlanStage);
			Vector3 vector2 = flightPlanStage.flight.Position(flightPlanStage.flight.apexTime);
			Vector3 endPos = new Vector3(vector2.x, racket.position.y, vector2.z);
			float num = durationTillStage2 + flightPlanStage.flight.apexTime;
			StopAllCoroutines();
			StartCoroutine(MoveToPos(endPos, 40f));
		}
	}

	private IEnumerator MoveToPos(Vector3 endPos, float speed)
	{
		Vector3 startPos = racket.position;
		float duration = Vector3.Distance(endPos, startPos) / speed;
		float time = 0f;
		while (time < duration)
		{
			time += Time.deltaTime;
			racket.position = Vector3.Lerp(startPos, endPos, time / duration);
			yield return null;
		}
	}
}
